package com.desktop.web.core.utils;

import java.util.Date;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Performs tasks using worker threads. It also allows tasks to be scheduled to be run at future
 * dates. This class mimics relevant methods in both {@link ExecutorService} and {@link Timer}. Any
 * {@link TimerTask} that's scheduled to be run in the future will automatically be run using the
 * thread executor's thread pool. This means that the standard restriction that TimerTasks should
 * run quickly does not apply.
 * 
 * @author baibai
 */
public final class TaskEngine {

    /** * The instance. */
    private static TaskEngine instance = new TaskEngine();

    /**
     * Returns a task engine instance (singleton).
     * 
     * @return a task engine.
     */
    public static TaskEngine getInstance() {
        return instance;
    }

    /** * The timer. */
    private Timer timer;

    /** * The executor. */
    private ExecutorService executor;

    /** * The wrapped tasks. */
    private Map<TimerTask, TimerTaskWrapper> wrappedTasks = new ConcurrentHashMap<TimerTask, TimerTaskWrapper>();

    private TaskEngine() {
        timer = new Timer("TaskEngine-timer", true);
        executor = Executors.newCachedThreadPool(new ThreadFactory() {

            final AtomicInteger threadNumber = new AtomicInteger(1);

            public Thread newThread(Runnable runnable) {
                Thread thread = new Thread(Thread.currentThread().getThreadGroup(), runnable, "TaskEngine-pool-" + threadNumber.getAndIncrement(), 0);
                thread.setDaemon(true);
                if (thread.getPriority() != Thread.NORM_PRIORITY) {
                    thread.setPriority(Thread.NORM_PRIORITY);
                }
                return thread;
            }
        });
    }

    public Future<?> submit(Runnable task) {
        return executor.submit(task);
    }


    public void schedule(TimerTask task, long delay) {
        timer.schedule(new TimerTaskWrapper(task), delay);
    }

    public void schedule(TimerTask task, Date time) {
        timer.schedule(new TimerTaskWrapper(task), time);
    }

    public void schedule(TimerTask task, long delay, long period) {
        TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task);
        wrappedTasks.put(task, taskWrapper);
        timer.schedule(taskWrapper, delay, period);
    }

    public void schedule(TimerTask task, Date firstTime, long period) {
        TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task);
        wrappedTasks.put(task, taskWrapper);
        timer.schedule(taskWrapper, firstTime, period);
    }

    public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
        TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task);
        wrappedTasks.put(task, taskWrapper);
        timer.scheduleAtFixedRate(taskWrapper, delay, period);
    }

    public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) {
        TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task);
        wrappedTasks.put(task, taskWrapper);
        timer.scheduleAtFixedRate(taskWrapper, firstTime, period);
    }

    /**
     * Cancels the execution of a scheduled task. {@link java.util.TimerTask#cancel()}
     * 
     * @param task the scheduled task to cancel.
     */
    public void cancelScheduledTask(TimerTask task) {
        TaskEngine.TimerTaskWrapper taskWrapper = wrappedTasks.remove(task);
        if (taskWrapper != null) {
            taskWrapper.cancel();
        }
    }

    /**
     * Shuts down the task engine service.
     */
    public void shutdown() {
        if (executor != null) {
            executor.shutdown();
            executor = null;
        }

        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }

    private class TimerTaskWrapper extends TimerTask {

        /** * The task. */
        private TimerTask task;

        /**
         * Instantiates a new timer task wrapper.
         * 
         * @param task the task
         */
        public TimerTaskWrapper(TimerTask task) {
            this.task = task;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void run() {
            executor.submit(task);
        }
    }
}
